package site.tuluan.sp4j.common.core.component.shiro.filter;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.BearerHttpAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import site.tuluan.sp4j.common.core.domain.AjaxResult;
import site.tuluan.sp4j.common.utils.ExceptionUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;

public class JwtFilter extends BearerHttpAuthenticationFilter {
    private static final Logger log = LoggerFactory.getLogger(JwtFilter.class);

    private static final String failureKeyAttribute = "shiroLoginFailure";

    /**
     * 登录认证失败处理
     * @param token token
     * @param e e
     * @param request request
     * @param response response
     * @return false
     */
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        // 添加认证失败信息，用于返回给客户端
        request.setAttribute(failureKeyAttribute, ExceptionUtils.getRootExceptionMessage(e));
        return false;
    }

    /**
     * 自定义访问被拒时的返回数据
     * @param request request
     * @param response response
     * @return return
     * @throws Exception Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        boolean loggedIn = super.onAccessDenied(request, response);

        if (!loggedIn) {
            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            PrintWriter writer = response.getWriter();

            HttpServletRequest httpRequest = WebUtils.toHttp(request);
            AjaxResult ajaxResult;
            // 如果是访问退出登录接口，则认证失败也返回成功消息
            // 否则返回数据：code:401 msg:认证失败信息
            if ("/logout".equals(httpRequest.getServletPath())) {
                ajaxResult = AjaxResult.buildSuccessResult();
            } else {
                String msg = request.getAttribute(failureKeyAttribute) != null ?
                        request.getAttribute(failureKeyAttribute).toString() : "认证失败";
                ajaxResult = AjaxResult.buildFailureResult(HttpStatus.UNAUTHORIZED.value(), msg);
            }

            ObjectMapper objectMapper = new ObjectMapper();
            try(JsonGenerator jsonGenerator = objectMapper.getFactory().createGenerator(writer)){
                jsonGenerator.writeObject(ajaxResult);
                writer.flush();
            }catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }

        return loggedIn;
    }
}
